package cn.edu.lzu.unitweb.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import cn.edu.lzu.unitweb.listener.ExaminerListener;
import cn.edu.lzu.unitweb.realm.ExaminerRealm;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import javax.annotation.Resource;
import java.util.*;

/**
 * @PackageName: cn.edu.lzu.unitweb.config
 * @ClassName: ShiroConfig
 * @Description: TODO shiro的配置
 * @Author: 张琦[QQ:3098086691]
 * @Date: 2019/11/28 10:43
 * @Version: 1.0
 */

@Configuration
public class ShiroConfig {
    @Resource
    private ExaminerListener examinerListener;
    @Autowired
    private ExaminerSessionDao examinerSessionDao;

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(getSecurityManager());

        //设置过滤器链
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/data/**", "anon");
        filterChainDefinitionMap.put("/images/**", "anon");
        filterChainDefinitionMap.put("/lib/**", "anon");
        filterChainDefinitionMap.put("/checkExaminerLogin", "anon");////////////////////////////////////
        filterChainDefinitionMap.put("/forgetPwd", "anon");
        filterChainDefinitionMap.put("/findUserPwd", "anon");
        filterChainDefinitionMap.put("/findExaminerPwd", "anon");
        /*filterChainDefinitionMap.put("/logout", "logout");*/
        filterChainDefinitionMap.put("/", "authc");
        filterChainDefinitionMap.put("/**", "authc");

        shiroFilterFactoryBean.setLoginUrl("/unitLogin");
        shiroFilterFactoryBean.setSuccessUrl("/unitIndex");
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * @Author: 张琦[QQ:3098086691]
     * @Description: TODO 安全管理器
     * @Date: 2019/11/28 11:21
     * @Param: []
     * @Return: org.apache.shiro.web.mgt.DefaultWebSecurityManager
     **/
    @Bean
    public DefaultWebSecurityManager getSecurityManager() {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        //设置‘用户认证器’，它通过调用realm判断当前登录的用户身份
        defaultWebSecurityManager.setAuthenticator(getModularRealmAtuhenticator());
        //设置realm
        Collection<Realm> realms = new ArrayList<>();
        //要使用注入到spring中的bean
        realms.add(getExaminerRealm());
        defaultWebSecurityManager.setRealms(realms);
        //设置‘会话管理器’
        defaultWebSecurityManager.setSessionManager(getSessionManager());
        return defaultWebSecurityManager;
    }

    /**
     * @Author: 张琦[QQ:3098086691]
     * @Description: TODO 用户认证器，设置认证策略设置为至少有一个realm认证成功
     * @Date: 2019/11/28 12:24
     * @Param: []
     * @Return: org.apache.shiro.authc.pam.ModularRealmAuthenticator
     **/
    @Bean
    public ModularRealmAuthenticator getModularRealmAtuhenticator() {
        ModularRealmAuthenticator modularRealmAuthenticator = new ModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        return modularRealmAuthenticator;
    }

    /**
     * @Author: 张琦[QQ:3098086691]
     * @Description: TODO 设置会话管理器
     * @Date: 2019/11/28 12:34
     * @Param: []
     * @Return: org.apache.shiro.session.mgt.SessionManager
     **/
    @Bean
    public SessionManager getSessionManager() {
//        SessionManager是抽象类，它的三个实现类分别是：
//        1)：DefaultSessionManager, 使用默认的实现，用于javaSE环境
//        2)：ServletContainerSessionManager, 用于web环境，其直接使用servlet容器的会话
//        3)：DefaultWebSessionManager, 用于Web环境的实现，可以替代ServletContainerSessionManager，自己维护着会话，直接废弃了Servlet容器的会话管理。
//        DefaultWebSessionManager defaultWebSessionManager =  new DefaultWebSessionManager();
        DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
        //设置session失效时间，默认30分钟；这里暂时设置为测试时间
        defaultWebSessionManager.setGlobalSessionTimeout(1000*60*60*4);
        //删除失效的session
        defaultWebSessionManager.setDeleteInvalidSessions(true);
        //注入自定义的监听器
        Collection<SessionListener> listeners=new ArrayList<>();
        listeners.add(examinerListener);
        defaultWebSessionManager.setSessionListeners(listeners);
        //设置自己的sessionDao
        defaultWebSessionManager.setSessionDAO(examinerSessionDao);

        return defaultWebSessionManager;
    }

    /**
     * @Author: 张琦[QQ:3098086691]
     * @Description: TODO 自定义realm：ExaminerRealm
     * @Date: 2019/11/28 13:33
     * @Param: []
     * @Return: cn.edu.lzu.unitweb.realm.ExaminerRealm
     **/
    @Bean
    public ExaminerRealm getExaminerRealm() {
        ExaminerRealm examinerRealm = new ExaminerRealm();
        //设置凭证匹配器，设定加密算法和迭代次数
        /*examinerRealm.setCredentialsMatcher(getHashedCredentialsMatcher());*/
        return examinerRealm;
    }

    /**
     * @Author: 张琦[QQ:3098086691]
     * @Description: TODO 凭证匹配器
     * @Date: 2019/11/28 13:41
     * @Param: []
     * @Return: org.apache.shiro.authc.credential.HashedCredentialsMatcher
     **/
    @Bean
    public HashedCredentialsMatcher getHashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher=new HashedCredentialsMatcher();
        //加密算法
        hashedCredentialsMatcher.setHashAlgorithmName("Md5");
        //迭代次数
        /*hashedCredentialsMatcher.setHashIterations(5);*/
        //false:默认的Base64编码；true：Hex编码
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(false);
        return hashedCredentialsMatcher;
    }

    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(getSecurityManager());
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
        Properties properties = new Properties();

        /*未授权处理页*/
        properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/403");
        resolver.setExceptionMappings(properties);
        return resolver;
    }


}
